package com.zte.claa.inficombo.csa.app.service.rptparse;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.Invocable;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommService;
import com.zte.claa.inficombo.csa.app.model.dev.ClaaDevInitState;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevCmdConfData;
import com.zte.claa.inficombo.csa.app.model.dev.DevRunState;
import com.zte.claa.inficombo.csa.app.model.redis.AlarmData;
import com.zte.claa.inficombo.csa.app.model.version.DevVersionUpdateLog;
import com.zte.claa.inficombo.csa.app.service.gprs.GPRSDevService;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSMessage;
import com.zte.claa.inficombo.csa.app.service.gprs.mina.GPRSDevConnSession;
import com.zte.claa.inficombo.csa.app.service.gprs.mina.GPRSDevServerManager;
import com.zte.claa.inficombo.csa.app.service.jsplugin.JSParam;
import com.zte.claa.inficombo.csa.app.service.nbiot.IotLocUpdataProcessService;
import com.zte.claa.inficombo.csa.app.service.rptparse.AppCmdWrapper;
import com.zte.claa.inficombo.csa.app.service.rptparse.AppCommand;
import com.zte.claa.inficombo.csa.app.service.rptparse.GspSubCrossMPKData;
import com.zte.claa.inficombo.csa.app.service.rptparse.LocRawData;
import com.zte.claa.inficombo.csa.app.service.rptparse.MultiPackageData;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserUpdata;
import com.zte.claa.inficombo.csa.app.service.rptparse.SensorOrgData;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.UpDataSubParser;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.claa.ConfDataRequest;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;

/** 
 * @author 0283000115
 * 
 * 备注：
 * 本解析子类，主要用于fntype=256、512两种类型的克拉自研GSP/SSP设备及其关联的传感终端的数据解析。
 * 根据接口规范约定，GSP/SSP及其连接的传感终端，数据解析主要流程包括如下几步：
 * 1、分包接收及组包；
 * 2、数据报文一次解析（组包完整后的初次解析，主要拆解为各sensor对应的原始码流，GSP/SSP自身数据解析出来）；
 * 3、各传感设备子报文二次解析（根据一次解析的结果识别出各sensor，调用对应js插件做二次解析）；
 * 4、解析结果持久化及发布。
 */
public class ParserGPRSUpdata extends ParserUpdata implements UpDataSubParser {
	// subparser类型key.
	public static final String PARSER_TP_CLAADTU = "sp_claa_dtu_parser";
	
	// GSP/SSP对应的js插件中必须实现的总包数请求命令处理函数对应的参数名.
	public static final String GSP_INIT_PKGNUM_CMD_PARA = "pkgnum";
	
	// 一次解析的中间结果类型标记.
	public static final String UPD_KEY_SENSORDATA  = "sensorgdata";
	public static final String UPD_KEY_SENSORDATALIST = "sensordatalist";
	
	private GPRSDevService gprsDevService; 
	
	// 定位解析数据处理...
	private IotLocUpdataProcessService locUpdataProcessService;

	// 日志记录.
	private Logger logger = LoggerFactory.getLogger(ParserGPRSUpdata.class);
	
	//protected ParserContainer container;

	@Override
	public String getParserType() {
		return PARSER_TP_CLAADTU;
	}

	@Override
	public void parseUpData(JSONObject json, DevBase dev, CSIFCommService svr) throws Exception {
		//this.parseUpRptData(json, dev, svr);
	} 
	
	public void parseGPRSData(GPRSMessage msg) throws Exception {
		if(msg == null){
			logger.error("[*@@*]GPRS Message is null!");
			return;
		}
		String devEui = msg.getHead().getDeveui();
		logger.debug("Begin parse updata: "+devEui);
		
		// 2.根据deveui---》查询缓存---》devtype，projectid，appeui等信息。
		DevBase dev = this.container.getCacheManager().getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			return;
		}
		// 添加到dev-run-state队列中，更新dev的runstate=online...
		DevRunState runState = new DevRunState();
		runState.setDevEUI(devEui);
		// 上报数据即可认为online...
		runState.setRunState(DevBase.RUN_STATE_ONLINE);
		this.container.addDevRunStateData(runState);
		
		// 开始解析数据...		
		// 只有GSP设备类型需要支持GPRS
		parseGPRSUpRptData(dev,msg);
		
	}
	
	/**
	 * 上报数据默认解析方法
	 * 
	 * @param json
	 * @param dev
	 * @param svr
	 * @throws Exception
	 * @return 0:正常解析，不分包；1-分包；
	 * 
	 */
	@SuppressWarnings("unchecked")
	protected int parseGPRSUpRptData(DevBase dev, GPRSMessage msg) throws Exception {
		String devTypeId = dev.getDevType();
		// 3.根据devtype获取解析插件（缓存）
		Invocable jsRunner = this.container.getJsPnpService().getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			return 0;
		}
		// 解析其他payload数据.
		String devEui = dev.getDevEui();
		String portStr = "0";
		// 自研lora定位产品会有detail信息.
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(devEui);
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(portStr);
		jsParam.setAppeui(dev.getAppEui());
		jsParam.setProjectid(dev.getProjectId());
		jsParam.appendPara(JSParam.UPDATA_PARA_NAME, msg.getBody().getData());
		// 如果涉及分包，则尝试设置分包中间结果.
		MultiPackageData mpd = this.container.getMultiPkgData(devEui);
		if(mpd != null){
			jsParam.appendPara(JSParam.MULTIPKG_PARA_NAME, mpd);
		}		
		// 模型附加配置信息
		String devExtData = dev.getDatajson();
		if (devExtData != null && !devExtData.trim().equals("")) {
			try {
				jsParam.setDevExtDatas(JsonUtils.jsonStrToHashMap(devExtData));
			} catch (Exception err) {
				logger.error("Get devextdata failed: " + devExtData + ", err=" + err.getMessage());
			}
		}
		// 设置持久化服务...
		jsParam.setPersistSvr(this.container.getPersisService());
		Object rptData = jsRunner.invokeFunction(JSParam.UPDATA_PARSE_FUNC, jsParam);
		// 情况1--解析结果无效；
		if(rptData == null){
			logger.info("[*!*]Invalid or nouse report data!");
			return 0;
		}
		// 情况2--只有alarm数据；	
		else if(AlarmData.class.isInstance(rptData)){
			logger.debug("Alarm data return from js plugin: "+rptData);
			this.parseAlarmData((AlarmData)rptData);
			return 0;
		}		
		// 情况3--只有metedata数据；
		else if(String.class.isInstance(rptData)){
			logger.debug("Mete data return from js plugin: "+rptData.toString());
			this.parseMeteData(dev, rptData.toString(), portStr);
			return 0;
		}		
		// 情况4--同时有metedata、alarm数据、ackcmd（一种或几种兼有）；
		else if(HashMap.class.isInstance(rptData)){
			Map<String, Object> map = (HashMap<String, Object>)rptData;
			// 4.1单条告警...
			Object ao = map.get(UPD_KEY_ALARM);
			if (ao != null && AlarmData.class.isInstance(ao)) {
				logger.debug("Alarm data return from js plugin: " + ao);
				try {
					this.parseAlarmData((AlarmData) ao);
				} catch (Exception ex) {
					logger.error("parseAlarmData failed: " + ao, ex);
				}
			}
			// 4.2告警列表...
			Object aoList = map.get(UPD_KEY_ALARMLIST);
			if (aoList != null && ArrayList.class.isInstance(aoList)) {
				logger.debug("Alarm data list return from js plugin: " + aoList);
				ArrayList<AlarmData> alarmList = (ArrayList<AlarmData>) aoList;
				for (AlarmData alarm : alarmList) {
					try {
						this.parseAlarmData(alarm);
					} catch (Exception ex) {
						logger.error("parseAlarmData failed: " + alarm, ex);
					}
				}
			}		
			// 4.3单条mdata（仅限于模型字段的json字符串）...
			Object mo = map.get(UPD_KEY_METEDATA);
			if (mo != null && String.class.isInstance(mo)) {
				logger.debug("Mete data return from js plugin: " + mo);
				try {
					this.parseMeteData(dev, mo.toString(), portStr);
				} catch (Exception ex) {
					logger.error("parseMeteData failed: " + mo, ex);
				}
			}
			// 4.4mdata列表（仅限于模型字段的json字符串）...
			Object moList = map.get(UPD_KEY_METEDATALIST);
			if (moList != null && ArrayList.class.isInstance(moList)) {
				logger.debug("Mete data list return from js plugin: " + moList);
				ArrayList<String> mdataList = (ArrayList<String>) moList;
				this.parseMeteDataByBatch(dev, mdataList, portStr);
			}
			// 4.5个别设备需要回复响应确认...
			Object ackCmd = map.get(UPD_KEY_ACKCMD);
			if (ackCmd != null && AppCmdWrapper.class.isInstance(ackCmd)) {
				logger.debug("Ack command return from js plugin: " + ackCmd);
				try {
					AppCmdWrapper cmdWrapper = (AppCmdWrapper)ackCmd;
					// send cmd in async way...
					this.container.sendDevCtrlCmd(cmdWrapper);
				} catch (Exception ex) {
					logger.error("Send ack cmd failed: " + ackCmd, ex);
				}
			}
			// 4.6 claa自研设备，初次入网时请求下发配置参数...
			Object confDataReqObj = map.get(UPD_KEY_CONFREQ);
			if (confDataReqObj != null && ConfDataRequest.class.isInstance(confDataReqObj)){
				logger.debug("claa dev req cmd conf data when first join: "+confDataReqObj);
				try{
					ConfDataRequest req = (ConfDataRequest)confDataReqObj;
					req.setDevtype(devTypeId);
					this.container.getDevConfDataSenderService().submitConfDataRequest(req);
				}catch(Exception ex){
					logger.error("submitConfDataRequest failed: "+confDataReqObj, ex);
				}
			}
			// 4.7 自研融合定位终端数据
			Object ldo = map.get(UPD_KEY_LOCRAWDATA);
			if (ldo != null && LocRawData.class.isInstance(ldo)) {
				logger.debug("Claa loc raw data return from js plugin: " + ldo);
				try {
					this.parseClaaLocRawData((LocRawData)ldo, dev);
				} catch (Exception ex) {
					logger.error("parseClaaLocRawData failed: " + ldo, ex);
				}
			}
			// 4.8 设备升级消息上报：包括升级准备命令响应、升级结果响应等  
			Object verupdateLog = map.get(UPD_KEY_DEVVERLOG);
			if (verupdateLog != null && DevVersionUpdateLog.class.isInstance(verupdateLog)) {
				logger.debug("Dev version update rsp return from js plugin: " + verupdateLog);
				try {
					this.processDevVerUpdateData((DevVersionUpdateLog)verupdateLog, dev);
				} catch (Exception ex) {
					logger.error("processDevVerUpdateData failed: " + verupdateLog, ex);
				}
			}			
			// 4.9 个别涉及分包情况.
			Object multiPkgObj = map.get(UPD_KEY_MULTIPKG);
			if(multiPkgObj != null && MultiPackageData.class.isInstance(multiPkgObj)){
				logger.debug("Multipackage data return from js plugin: " + multiPkgObj);
				try {
					MultiPackageData tmpMpd = (MultiPackageData)multiPkgObj;
					tmpMpd.setAppeui(dev.getAppEui());
					tmpMpd.setDeveui(devEui);
					tmpMpd.setDevName(dev.getDevName());
					tmpMpd.setDevtype(devTypeId);
					tmpMpd.setPortStr(portStr);
					tmpMpd.setProjectid(dev.getProjectId());					
					this.processMultiPkgData(tmpMpd, jsRunner, dev, portStr);
				} catch (Exception ex) {
					logger.error("Multipackage data process failed: " + multiPkgObj, ex);
				}
				return 1;
			}
		}
		// 单个分包情况，JS插件基本上不会这么返回.
		else if (MultiPackageData.class.isInstance(rptData)){
			logger.debug("Multi-package data return from js plugin: "+rptData.toString());
			MultiPackageData tmpMpd = (MultiPackageData)rptData;
			tmpMpd.setAppeui(dev.getAppEui());
			tmpMpd.setDeveui(devEui);
			tmpMpd.setDevName(dev.getDevName());
			tmpMpd.setDevtype(devTypeId);
			tmpMpd.setPortStr(portStr);
			tmpMpd.setProjectid(dev.getProjectId());	
			this.processMultiPkgData(tmpMpd, jsRunner, dev, portStr);
			return 1;
		}
		// 自研设备初次入网会请求下发参数.
		else if (ConfDataRequest.class.isInstance(rptData)){
			logger.debug("claa dev req cmd conf data when first join: "+rptData.toString());
			ConfDataRequest req = (ConfDataRequest)rptData;
			req.setDevtype(devTypeId);
			this.container.getDevConfDataSenderService().submitConfDataRequest(req);
			return 0;
		}	
		else if (LocRawData.class.isInstance(rptData)){
			logger.debug("claa loc raw data rpt: "+rptData.toString());
			this.parseClaaLocRawData((LocRawData)rptData, dev);
			return 0;
		}
		else if (DevVersionUpdateLog.class.isInstance(rptData)){
			logger.debug("claa dev version udpate data rpt: "+rptData.toString());
			this.processDevVerUpdateData((DevVersionUpdateLog)rptData, dev);
			return 0;
		}
		else {
			logger.warn("[*!*]Result data is invalid from js-plugin for devtype: "+devTypeId+", data="+rptData);
		}
		return 0;
	}
	/**
	 * 克拉自研定位数据处理.
	 * @param locData
	 */
	protected void parseClaaLocRawData(LocRawData locData, final DevBase dev) {
		if(locData == null || dev == null){
			return;
		}
		// 处理定位数据上报（前提是有基于gprs的定位数据上报和js插件解析）
		this.locUpdataProcessService.parseClaaLocRawData(locData, dev);
		// 处理激活消息.
		if(locData.hasActivedData()){
			try{
				ClaaDevInitState state = new ClaaDevInitState();
				state.setConfirmactive(locData.getActived());
				state.setDeveui(dev.getDevEui());
				state.setDevtype(dev.getDevType());
				state.setProjectid(dev.getProjectId());
				// 这里改成异步提交方式，由devinstate处理线程统一处理.
				// this.container.getPersisService().saveClaaDevInitState(state);
				// logger.info("saveClaaDevInitState ok: "+state);
				this.container.getDevConfDataSenderService().submitClaaDevInitState(state);
			}catch(Exception e){
				logger.error("saveClaaDevInitState failed: "+dev.getDevEui()+", state="+locData.getActived(), e);
			}	
		}
	}

	/**
	 * 一、解析框架部分的主要逻辑：
	 * 1、如果当前MPD不空，则与返回结果合并(调用container.merge方法.)；
	 * 2、如果MPD.isDataOk=yes，isParsed=yes,confirm=yes，则此时JS已经做了metedata解析和ackcmd下发；MPD从缓存删除；调外部扩展接口；
	 * 3、如果MPD.isDataOk=yes，isParsed=yes,confirm=NO，  则此时JS已经做了metedata解析，框架侧走metedata解析流程；MPD入缓存；
	 * 4、如果MPD.isDataOk=yes，isParsed=no, confirm=NO，  则此时JS未做metedata解析，触发二次解析，设置isparsed=yes；MPD入缓存；
	 * 5、如果MPD.isDataOk=yes，isParsed=no, confirm=yes，则此时JS未做metedata解析，触发二次解析，设置isparsed=yes；MPD入缓存；
	 * 6、如果MPD.isDataOk=NO，confirm=yes，则此时JS已经做了ackcmd下发，框架侧走相应ACK流程；MPD入缓存；
	 * 7、如果MPD.isDataOk=NO，confirm=NO，此时MPD入缓存，等待后续包；
	 * 
	 * 二、相应地JS插件内部逻辑：（注意确认包序号是从1开始还是从0开始，JS-Framework约定从1开始）
	 * 1、接收原始byte[]流-bs，并尝试从JSParam中获取multipkgdata参数-mpd；
	 * 2、如果mpd参数为空，则创建一个mpd对象；
	 * 3、当前bs是普通分包数据包，则解析分包数据，确定包序号；
	 * 	   3.1 将包append到mpd中，注意：判断是否出现重复包，如果出现则将mpd重新初始化；
	 * 	   3.2 判断mpd是否dataok；
	 *     3.3 如果dataok，则调用buildData()解析出metedata（本次GNSS设备不需要，其他普通分包类型的情况需要），设置isparsed=yes；
	 *     3.4返回mpd及metedata（本次GNSS不需要），特别地，如果GNSS服务器只需要GNSS终端的净荷，还涉及data的特殊处理.
	 * 4、当前bs是分包上行校验包（分包发送完毕后的确认消息）；
	 *	   4.1 设置mpd的confirmed值=1；
	 *	   4.2 获取确认位图（getBitmap）及构造ackcmd，使用全1模式（ignoreabsentpkg=true）；
	 *	   4.3返回mpd和ackcmd；
	 * 5、新增二次解析方法入口： UPDATA_MULTIPKG_REPARSE_FUNC = "funcmultipkgreparse";
	 * 	   5.1框架针对分包的类型调用，入参是mpd；
	 *     5.2调用buildData()，得到完整码流，解析码流返回metedata（可选）和mpd（设置isparsed=yes）；
	 *     
	 * 三、针对GSP多sensor交叉分包上报的情况：
	 * 框架在MultiPackageData内部扩展了sensor-->GspSubCrossMPKData的映射表，用于分别处理各sensor自己的多包.
	 * 此时，MultiPackageData实例的模式是2-多sensor交叉分包模式。
	 * 对于这种情况，专门开启分支处理，详细见下方法。
	 * 此外，对于丢包的情况，目前处理策略仍然不能很好地处理“残存”的包，基本上是丢弃。
	 */
	protected void processMultiPkgData(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		if(mpd == null){
			return;
		}
		String devEui = dev.getDevEui();
		// 加锁互斥，避免重复处理. 
		synchronized(devEui) {
			MultiPackageData mergeData = this.container.mergePackageData(mpd);
			if(mergeData == null){
				return;
			}
			// 1.非多sensor交叉多包模式.
			boolean isConfirmed = (mergeData.getIsConfirmed() == MultiPackageData.CONFIRMED_YES);
			boolean isDataOk = mergeData.isDataOk();
			boolean isParsed = (mergeData.getIsParsed() == MultiPackageData.PARSED_YES);
			if(!mergeData.isGspCrossMpkMode()){
				if (isDataOk && isParsed && isConfirmed) {
					this.container.removeMultiPkgData(mpd.getDeveui());
				} else if (isDataOk && !isParsed) {
					// 首先设置解析标记.
					mergeData.setIsParsed(MultiPackageData.PARSED_YES);
					this.container.setMultiPkgData(mergeData);
					// 分包收集完整后进行二次解析
					this.reParseMultiPkgData(mergeData, jsRunner, dev, portStr);
					this.container.removeMultiPkgData(mpd.getDeveui());
					logger.info("reParseMultiPkgData: " + mergeData);
				} else if (isDataOk && isParsed && !isConfirmed) {
					this.container.setMultiPkgData(mergeData);
				} else if (!isDataOk && isConfirmed) {
					this.container.setMultiPkgData(mergeData);
				} else if (!isDataOk && !isConfirmed) {
					this.container.setMultiPkgData(mergeData);
				}
			}
			// 2.多sensor交叉多包模式.
			else if(mergeData.isGspCrossMpkMode()){
				logger.info("[@@]try to process GspCrossMpkMode: "+mergeData.printCrossMpkData());
				this.checkAndProcessGspCrossMpk(mergeData, jsRunner, dev, portStr);
				logger.info("[@@]finish processing GspCrossMpkMode for dev: "+dev.getDevEui());
			}
		}
	}
	
	// 多sensor交叉多包模式处理.
	private void checkAndProcessGspCrossMpk(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		List<String> sensorIdList = mpd.getAllGspSubCrossMpkMapKeys();
		if(sensorIdList == null || sensorIdList.size() == 0){
			this.container.removeMultiPkgData(mpd.getDeveui());
			logger.info("no GspSubCrossMPKData exists!");
			return;
		}
		for(String sensorId : sensorIdList){
			GspSubCrossMPKData crossMpkData = mpd.getGspSubCrossMPKData(sensorId);
			if(crossMpkData == null){
				// mpd.removeGspSubCrossMPKData(sensorId);
				continue;
			}
			if(crossMpkData.isDataOk() || crossMpkData.isDataExpire(ParserContainer.SENSOR_PKG_EXPTIME)){
				try{
					SensorOrgData sensorOrgData = new SensorOrgData();
					sensorOrgData.setDtueui(dev.getDevEui());
					sensorOrgData.setSensorid(sensorId);
					sensorOrgData.setPayload(crossMpkData.buildData());
					this.reParseSensorOrgData(sensorOrgData, dev, portStr);
					logger.info("[^_^]reParseSensorOrgData ok: gsp={}, sensor={}.", dev.getDevEui(), sensorId);
				}catch(Exception e){
					logger.error("[!Cross!]reParseSensorOrgData failed: "+dev.getDevEui()+", data="+crossMpkData, e);
				}
				// 单个sensor处理完毕就从里层映射表移除.
				mpd.removeGspSubCrossMPKData(sensorId);
				continue;
			}
		}
		// 检查是否都处理完毕.
		if(mpd.getCurrentCrossMPKDataNum() == 0){
			this.container.removeMultiPkgData(mpd.getDeveui());
		} else {
			this.container.setMultiPkgData(mpd);
		}
	}

	/**
	 * 分包收集完整后进行补充解析
	 * 
	 * @param mpd
	 * @param jsRunner
	 */
	@SuppressWarnings("unchecked")
	protected void reParseMultiPkgData(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		// 设置可能的扩展配置数据，供js插件访问.
		this.setExtDataToMultiPkg(mpd, dev);
		// 组包完整后的一次解析.
		Object rptData = jsRunner.invokeFunction(JSParam.UPDATA_MULTIPKG_REPARSE_FUNC, mpd);
		// 情况1--解析结果无效；
		if(rptData == null){
			logger.info("[*!*]MPD-Reparse: Invalid or nouse report data!");
			return;
		}
		// 情况2--只有alarm数据；	
		else if(AlarmData.class.isInstance(rptData)){
			logger.debug("MPD-Reparse: Alarm data return from js plugin: "+rptData);
			this.parseAlarmData((AlarmData)rptData);
			return;
		}		
		// 情况3.1--只有metedata数据；
		else if(String.class.isInstance(rptData)){
			logger.debug("MPD-Reparse: Mete data return from js plugin: "+rptData.toString());
			this.parseMeteData(dev, rptData.toString(), portStr);
			return;
		}	
		// 3.2自研设备初次入网会请求下发参数.
		else if (ConfDataRequest.class.isInstance(rptData)){
			logger.debug("claa dev req cmd conf data when first join: "+rptData.toString());
			ConfDataRequest tmpReq = (ConfDataRequest)rptData;
			tmpReq.setDevtype(dev.getDevType());
			this.processGspInitConfDataReq(tmpReq, dev.getDevType());
			return;
		}
		// 3.3 SensorOrgData 二次解析.
		else if (SensorOrgData.class.isInstance(rptData)){
			logger.debug("claa sensor dev data reparse: "+rptData.toString());
			this.reParseSensorOrgData((SensorOrgData)rptData, dev, portStr);
			return;
		}
		// 3.4 GSP设备升级响应消息.
		else if (DevVersionUpdateLog.class.isInstance(rptData)){
			logger.debug("claa dev version udpate data rpt: "+rptData.toString());
			this.processDevVerUpdateData((DevVersionUpdateLog)rptData, dev);
			return;
		}
		// 3.4 GSP设备当前状态和版本号上报消息.
		else if (ClaaDevInitState.class.isInstance(rptData)){
			logger.debug("claa gsp ClaaDevInitState data rpt: "+rptData.toString());
			this.processClaaDevInitState((ClaaDevInitState)rptData, dev);
			return;
		}	
		// 情况4--同时有metedata、alarm数据、ackcmd（一种或几种兼有）；
		else if(HashMap.class.isInstance(rptData)){
			Map<String, Object> map = (HashMap<String, Object>)rptData;
			// 4.1单条告警...
			Object ao = map.get(UPD_KEY_ALARM);
			if (ao != null && AlarmData.class.isInstance(ao)) {
				logger.debug("Alarm data return from js plugin: " + ao);
				try {
					this.parseAlarmData((AlarmData) ao);
				} catch (Exception ex) {
					logger.error("parseAlarmData failed: " + ao, ex);
				}
			}
			// 4.2告警列表...
			Object aoList = map.get(UPD_KEY_ALARMLIST);
			if (aoList != null && ArrayList.class.isInstance(aoList)) {
				logger.debug("Alarm data list return from js plugin: " + aoList);
				ArrayList<AlarmData> alarmList = (ArrayList<AlarmData>) aoList;
				for (AlarmData alarm : alarmList) {
					try {
						this.parseAlarmData(alarm);
					} catch (Exception ex) {
						logger.error("parseAlarmData failed: " + alarm, ex);
					}
				}
			}		
			// 4.3单条mdata（仅限于模型字段的json字符串）...
			Object mo = map.get(UPD_KEY_METEDATA);
			if (mo != null && String.class.isInstance(mo)) {
				logger.debug("Mete data return from js plugin: " + mo);
				try {
					this.parseMeteData(dev, mo.toString(), portStr);
				} catch (Exception ex) {
					logger.error("parseMeteData failed: " + mo, ex);
				}
			}
			// 4.4mdata列表（仅限于模型字段的json字符串）...
			Object moList = map.get(UPD_KEY_METEDATALIST);
			if (moList != null && ArrayList.class.isInstance(moList)) {
				logger.debug("Mete data list return from js plugin: " + moList);
				ArrayList<String> mdataList = (ArrayList<String>) moList;
				this.parseMeteDataByBatch(dev, mdataList, portStr);
			}			
			// 4.5 SensorOrgData的二次解析.
			Object sdo = map.get(UPD_KEY_SENSORDATA);
			if (sdo != null && SensorOrgData.class.isInstance(sdo)) {
				logger.debug("Sensor org data return from js plugin: " + sdo);
				try {
					this.reParseSensorOrgData((SensorOrgData)sdo, dev, portStr);
				} catch (Exception ex) {
					logger.error("parseSensorOrgData failed: " + sdo, ex);
				}
			}
			// 4.6SensorOrgData列表...
			Object sdoList = map.get(UPD_KEY_SENSORDATALIST);
			if (sdoList != null && ArrayList.class.isInstance(sdoList)) {
				logger.debug("Sensor org data list return from js plugin: " + sdoList);
				ArrayList<SensorOrgData> sensorOrgDataList = (ArrayList<SensorOrgData>) sdoList;
				for(SensorOrgData sensorOrgData : sensorOrgDataList){
					try {
						this.reParseSensorOrgData(sensorOrgData, dev, portStr);
					} catch (Exception ex) {
						logger.error("[2]parseSensorOrgData failed: " + sensorOrgData, ex);
					}
				}
			}		
			// 4.7个别设备需要回复响应确认...
			Object ackCmd = map.get(UPD_KEY_ACKCMD);
			if (ackCmd != null && AppCmdWrapper.class.isInstance(ackCmd)) {
				logger.debug("Ack command return from js plugin: " + ackCmd);
				try {
					AppCmdWrapper cmdWrapper = (AppCmdWrapper)ackCmd;
					// send cmd in async way...
					this.container.sendDevCtrlCmd(cmdWrapper);
				} catch (Exception ex) {
					logger.error("Send ack cmd failed: " + ackCmd, ex);
				}
			}
			// 4.8 claa自研设备，初次入网时请求下发配置参数...
			Object confDataReqObj = map.get(UPD_KEY_CONFREQ);
			if (confDataReqObj != null && ConfDataRequest.class.isInstance(confDataReqObj)){
				logger.debug("claa dev req cmd conf data when first join: "+confDataReqObj);
				try{
					this.processGspInitConfDataReq((ConfDataRequest)confDataReqObj, dev.getDevType());
				}catch(Exception ex){
					logger.error("processGspInitConfDataReq failed: "+confDataReqObj, ex);
				}
			}
			// 4.9 设备升级消息上报：包括升级准备命令响应、升级结果响应等  
			Object verupdateLog = map.get(UPD_KEY_DEVVERLOG);
			if (verupdateLog != null && DevVersionUpdateLog.class.isInstance(verupdateLog)) {
				logger.debug("Dev version update rsp return from js plugin: " + verupdateLog);
				try {
					this.processDevVerUpdateData((DevVersionUpdateLog)verupdateLog, dev);
				} catch (Exception ex) {
					logger.error("processDevVerUpdateData failed: " + verupdateLog, ex);
				}
			}
			// 4.10 设备升级消息上报：包括升级准备命令响应、升级结果响应等  
			Object gspcurversion = map.get(UPD_KEY_GSPCURVER);
			if (gspcurversion != null && ClaaDevInitState.class.isInstance(gspcurversion)) {
				logger.debug("Dev cur version rpt return from js plugin: " + gspcurversion);
				try {
					this.processClaaDevInitState((ClaaDevInitState)rptData, dev);
				} catch (Exception ex) {
					logger.error("processClaaDevInitState failed: " + gspcurversion, ex);
				}
			}
		}
		else {
			logger.warn("[*!!*]1-Result data is invalid from js-plugin for devtype: "+dev.getDevType()+", data="+rptData);
		}
	}
	
	/**
	 * 传感终端数据的二次解析.
	 * 
	 * @param sensorData
	 * @param dev
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private void reParseSensorOrgData(SensorOrgData sensorData, DevBase dtudev, String portStr) throws Exception {
		if(sensorData == null){
			return;
		}
		sensorData.setDtueui(dtudev.getDevEui());
		String sensorEui = sensorData.getSensorEui();
		DevBase sensordev = this.container.getCacheManager().getDev(sensorEui);
		if(sensordev == null){
			logger.error("[*@@*]Sensor-Dev not exist in local cache/db: "+sensorEui);
			return;
		}
		String devTypeId = sensordev.getDevType();		
		Invocable jsRunner = this.container.getJsPnpService().getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]2-Can not find the JS plugin for devtype="+devTypeId+", sensor="+sensorEui);
			return;
		}
		byte[] payloadBS = sensorData.getPayload();
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(sensorEui);
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(portStr);
		jsParam.setAppeui(dtudev.getAppEui());
		jsParam.setProjectid(dtudev.getProjectId());
		jsParam.appendPara(JSParam.UPDATA_PARA_NAME, payloadBS);	
		// 模型附加配置信息
		String devExtData = sensordev.getDatajson();
		if (devExtData != null && !devExtData.trim().equals("")) {
			try {
				jsParam.setDevExtDatas(JsonUtils.jsonStrToHashMap(devExtData));
			} catch (Exception err) {
				logger.error("Get devextdata failed: " + devExtData + ", err=" + err.getMessage());
			}
		}
		// 设置持久化服务...
		jsParam.setPersistSvr(this.container.getPersisService());
		Object rptData = jsRunner.invokeFunction(JSParam.UPDATA_PARSE_FUNC, jsParam);
		// 情况1--解析结果无效；
		if(rptData == null){
			logger.info("[*!*]Invalid or nouse report data for sensor: "+sensorEui);
			return;
		}
		// 情况2--只有alarm数据；	
		else if(AlarmData.class.isInstance(rptData)){
			logger.debug("Alarm data return from js plugin: "+rptData);
			this.parseAlarmData((AlarmData)rptData);
			return;
		}		
		// 情况3.1--只有metedata数据；
		else if(String.class.isInstance(rptData)){
			logger.debug("Mete data return from js plugin: "+rptData.toString());
			this.parseMeteData(sensordev, rptData.toString(), portStr);
			return;
		}		
		// 3.2 自研设备初次入网会请求下发参数.
		else if (ConfDataRequest.class.isInstance(rptData)){
			logger.debug("claa dev req cmd conf data when first join: "+rptData.toString());
			ConfDataRequest tmpReq = (ConfDataRequest)rptData;
			tmpReq.setDevtype(dtudev.getDevType());
			this.processGspInitConfDataReq(tmpReq, dtudev.getDevType());
			return;
		}
		// 3.4 GSP设备升级响应消息（应该不会走到这一步）.
		else if (DevVersionUpdateLog.class.isInstance(rptData)){
			logger.debug("claa dev version udpate data rpt: "+rptData.toString());
			this.processDevVerUpdateData((DevVersionUpdateLog)rptData, dtudev);
			return;
		}
		// 情况4--同时有metedata、alarm数据、ackcmd等（一种或几种兼有）；
		else if(HashMap.class.isInstance(rptData)){
			Map<String, Object> map = (HashMap<String, Object>)rptData;
			// 4.1单条告警...
			Object ao = map.get(UPD_KEY_ALARM);
			if (ao != null && AlarmData.class.isInstance(ao)) {
				logger.debug("Alarm data return from js plugin: " + ao);
				try {
					this.parseAlarmData((AlarmData) ao);
				} catch (Exception ex) {
					logger.error("parseAlarmData failed: " + ao, ex);
				}
			}
			// 4.2告警列表...
			Object aoList = map.get(UPD_KEY_ALARMLIST);
			if (aoList != null && ArrayList.class.isInstance(aoList)) {
				logger.debug("Alarm data list return from js plugin: " + aoList);
				ArrayList<AlarmData> alarmList = (ArrayList<AlarmData>) aoList;
				for (AlarmData alarm : alarmList) {
					try {
						this.parseAlarmData(alarm);
					} catch (Exception ex) {
						logger.error("parseAlarmData failed: " + alarm, ex);
					}
				}
			}		
			// 4.3单条mdata（仅限于模型字段的json字符串）...
			Object mo = map.get(UPD_KEY_METEDATA);
			if (mo != null && String.class.isInstance(mo)) {
				logger.debug("Mete data return from js plugin: " + mo);
				try {
					this.parseMeteData(sensordev, mo.toString(), portStr);
				} catch (Exception ex) {
					logger.error("parseMeteData failed: " + mo, ex);
				}
			}
			// 4.4mdata列表（仅限于模型字段的json字符串）...
			Object moList = map.get(UPD_KEY_METEDATALIST);
			if (moList != null && ArrayList.class.isInstance(moList)) {
				logger.debug("Mete data list return from js plugin: " + moList);
				ArrayList<String> mdataList = (ArrayList<String>) moList;
				this.parseMeteDataByBatch(sensordev, mdataList, portStr);
			}
			// 4.5个别设备需要回复响应确认...
			Object ackCmd = map.get(UPD_KEY_ACKCMD);
			if (ackCmd != null && AppCmdWrapper.class.isInstance(ackCmd)) {
				logger.debug("Ack command return from js plugin: " + ackCmd);
				try {
					AppCmdWrapper cmdWrapper = (AppCmdWrapper)ackCmd;
					// send cmd in async way...
					this.container.sendDevCtrlCmd(cmdWrapper);
				} catch (Exception ex) {
					logger.error("Send ack cmd failed: " + ackCmd, ex);
				}
			}
			// 4.6 claa自研设备，初次入网时请求下发配置参数...
			Object confDataReqObj = map.get(UPD_KEY_CONFREQ);
			if (confDataReqObj != null && ConfDataRequest.class.isInstance(confDataReqObj)){
				logger.debug("claa dev req cmd conf data when first join: "+confDataReqObj);
				try{
					this.processGspInitConfDataReq((ConfDataRequest)confDataReqObj, dtudev.getDevType());
				}catch(Exception ex){
					logger.error("processGspInitConfDataReq failed: "+confDataReqObj, ex);
				}
			}
			// 4.8 设备升级消息上报：包括升级准备命令响应、升级结果响应等  
			Object verupdateLog = map.get(UPD_KEY_DEVVERLOG);
			if (verupdateLog != null && DevVersionUpdateLog.class.isInstance(verupdateLog)) {
				logger.debug("Dev version update rsp return from js plugin: " + verupdateLog);
				try {
					this.processDevVerUpdateData((DevVersionUpdateLog)verupdateLog, dtudev);
				} catch (Exception ex) {
					logger.error("processDevVerUpdateData failed: " + verupdateLog, ex);
				}
			}	
		}
		else {
			logger.warn("[*!!!*]2-Result data is invalid from js-plugin for devtype: "+devTypeId+", data="+rptData);
		}
	}
	
	/**
	 * 处理GSP/SSP的init配置参数请求.
	 * 一、主要逻辑： 对于普通的gsp-sensor初始化命令（单条命令不分包）
	 * 1、如果是总包数请求消息，直接构造响应命令下发.
	 * 2、如果是具体配置请求，则提交cmdconfdata下发服务下发。
	 * 
	 * 二、特别地，对于GSP-PT40这种传感器，单条命令中包括的CPNO测温点编号过多，需要分包（而且要求单包可解）：
	 * 1、请求总包数消息：CSA框架查询PT40的初始化命令（只能有一条），并且把命令参数以map的形式添加到AppCommand的参数map中；
	 * 2、请求单包消息：   CSA框架将ConfDataRequest中的 pkgindex 参数以extmap的形式传递到js中，供js参照拆包；
	 * 
	 * @param confReq
	 * @throws Exception
	 */
	private void processGspInitConfDataReq(ConfDataRequest confReq, String devtpId) throws Exception {
		if(confReq == null){
			logger.error("processGspInitConfDataReq req=null!");
			return;
		}
		logger.info("Begin process cmd conf req: "+confReq);
		// 总包数请求消息. 仅查询status=1（已启用的配置记录）
		if(confReq.getReqflag() == ConfDataRequest.REQ_FLAG_PKGNUM){
			if(confReq.getPkgmode() == ConfDataRequest.PKG_MODE_SPECIAL){
				this.processSpecialPkgNumRespone(confReq, devtpId);
			} else {
				this.processNormalPkgNumRespone(confReq, devtpId);
			}
		} else {
			// 如果是请求单个分包，需要将包序号（pkgIndex）携带到js中即可...
			String dtuEui = confReq.getDeveui();
			int pkgIndex = confReq.getPkgindex();
			DevCmdConfData confData = null;
			// 如果是特殊分包模式，则默认只能有一个初始化配置命令...
			if(confReq.getPkgmode() == ConfDataRequest.PKG_MODE_SPECIAL){
				List<DevCmdConfData> confDataList = this.container.getDevCmdConfMapper().queryDevCmdDataOfSensorByDtu(confReq.getDeveui());
				if(confDataList.size() > 0){
					confData = confDataList.get(0);	
					logger.info("confDataList size = "+confDataList.size());
				} else {
					logger.warn("[!**!]No cmd conf data for GSP/SSP(special-2): "+confReq);
				}
			} else {
				confData = this.container.getDevCmdConfMapper().queryDevCmdDataOfDtuEui(dtuEui, pkgIndex, DevCmdConfData.STATUS_ACTIVE);
			}
			if(confData != null){
				ConfDataRequest newReq = new ConfDataRequest();
				newReq.setAppeui(confReq.getAppeui());
				newReq.setCmdcode(confData.getCmdcode());
				newReq.setDeveui(confData.getDeveui());
				newReq.setDevport(confData.getDevport());
				newReq.setProjectid(confData.getProjectid());
				newReq.setDevtype(devtpId);
				// 这里记录实际请求的包序号和分包请求模式...
				newReq.setPkgindex(confReq.getPkgindex());
				newReq.setPkgmode(confReq.getPkgmode());
				// 不同于总包数请求响应命令，这里具体的GSP自身和其下各传感器实例的cmdconfdata通过专门下发服务下发.
				this.container.getDevConfDataSenderService().submitConfDataRequest(newReq);
				logger.info("[^_^]submitConfDataRequest ok: "+newReq);
			} else {
				logger.warn("[@@]Can NOT get DevCmdConfData for: dtuEui="+dtuEui+", pkgIndex="+pkgIndex);
			}			
		}
	}
	
	/**
	 * 普通的分包总数请求消息处理
	 * 
	 * @param confReq
	 * @param devtpId
	 * @throws Exception
	 */
	private void processNormalPkgNumRespone(ConfDataRequest confReq, String devtpId) throws Exception {
		int totalNum = this.container.getDevCmdConfMapper().queryDevCmdNumByDtuEui(confReq.getDeveui(), 
				DevCmdConfData.STATUS_ACTIVE);
		if(totalNum <= 0){
			logger.warn("[!**!]No cmd conf data for GSP/SSP: "+confReq);
			return; 
		}
		AppCommand reqCmd = new AppCommand();
		reqCmd.setCmd(confReq.getCmdcode());
		Map<String, Object> paras = new HashMap<String, Object>();
		paras.put(GSP_INIT_PKGNUM_CMD_PARA, totalNum);
		reqCmd.setParas(paras);
		AppCmdWrapper cmdWrapper = new AppCmdWrapper();
		cmdWrapper.setAppeui(confReq.getAppeui());
		// 统一按非确认命令下发.
		cmdWrapper.setConfirm(false);
		cmdWrapper.setDeveui(confReq.getDeveui());
		cmdWrapper.setDevtypeid(devtpId);
		cmdWrapper.setPayload(reqCmd);
		cmdWrapper.setPort(confReq.getDevport());
		this.container.sendDevCtrlCmd(cmdWrapper);
		logger.info("[^_^]sendDevCtrlCmd (normal) ok: "+cmdWrapper);
	}
	
	/**
	 * 特殊的分包总数请求处理（需要js内部根据初始化命令paras配置计算分包数目）
	 * 
	 * @param confReq
	 * @param devtpId
	 * @throws Exception
	 */
	private void processSpecialPkgNumRespone(ConfDataRequest confReq, String devtpId) throws Exception {
		List<DevCmdConfData> confDataList = this.container.getDevCmdConfMapper().queryDevCmdDataOfSensorByDtu(confReq.getDeveui());
		if(confDataList == null || confDataList.size() == 0){
			logger.warn("[!**!]No cmd conf data for GSP (special pkg): "+confReq);
			return; 
		}
		if(confDataList.size() > 1){
			logger.warn("Too much sensor config data for gsp: "+confReq);
		}
		// DevCmdConfData confData = this.container.getDevCmdConfMapper().queryDevCmdDataOfDtuEui(confReq.getDeveui(), 0, DevCmdConfData.STATUS_ACTIVE);
		DevCmdConfData confData = confDataList.get(0);	
		JSONObject json = new JSONObject(confData.getDatajson());
		Map<String, Object> cmdParaMap = null;
		JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
		if(jsonParaObj == null){
			cmdParaMap = new HashMap<String, Object>(0);
		} else {
			cmdParaMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
		}
		// 构造分包总数的请求响应命令，此时的命令参数是初始化命令的参数（供js内部计算分包数用）；
		AppCommand reqCmd = new AppCommand();
		reqCmd.setCmd(confReq.getCmdcode());
		// 此时CSA框架不再直接计算分包数目...
		// paras.put(GSP_INIT_PKGNUM_CMD_PARA, totalNum);
		reqCmd.setParas(cmdParaMap);
		AppCmdWrapper cmdWrapper = new AppCmdWrapper();
		cmdWrapper.setAppeui(confReq.getAppeui());
		// 统一按非确认命令下发.
		cmdWrapper.setConfirm(false);
		// cmdWrapper.setDeveui(confReq.getDeveui());
		// 这里换成sensor的deveui，将总包数计算逻辑从GSP转移到sensor的js中...
		cmdWrapper.setDeveui(confData.getDeveui());
		cmdWrapper.setDevtypeid(confData.getDevtpid());
		cmdWrapper.setPayload(reqCmd);
		cmdWrapper.setPort(confReq.getDevport());
		this.container.sendDevCtrlCmd(cmdWrapper);
		logger.info("[^_^]sendDevCtrlCmd (special) ok: "+cmdWrapper);
	}
	
	/**
	 * 处理GSP的ClaaDevInitState上报消息.
	 */
	public void processClaaDevInitState(ClaaDevInitState state, DevBase dev){
		// 补充JS插件中未设置的字段.
		state.setConfirmactive(ClaaDevInitState.IS_CONF_ACTIVED);
		state.setDeveui(dev.getDevEui());
		state.setDevtype(dev.getDevType());
		state.setProjectid(dev.getProjectId());
		if(null == this.gprsDevService){
			logger.info("[^_^]Function processClaaDevInitState, gprsDevService is null!");
			return; //Dereference after null check
		}
		GPRSDevServerManager gprsServerManager = this.gprsDevService.getConnSvrManager();
		if(null == gprsServerManager || null == gprsServerManager.getDeveuiSessionMap()){
			logger.info("[^_^]Function processClaaDevInitState, gprsServerManager or deveuiSessionMap is null!");
			return;  //Dereference after null check
		}
		String dtueui = dev.parseDtuDevEui();
		GPRSDevConnSession devconn = gprsServerManager.getDeveuiSessionMap().get(dtueui);
		String remark = state.getRemark();
		if(null != devconn){
			if(null != remark && !remark.equals("")){
				remark = "ICCID:"+devconn.getIccid() + ",RSSI:" + devconn.getRssi() + "," + remark;
			}else{
				remark = "ICCID:"+devconn.getIccid() + ",RSSI:" + devconn.getRssi();
			}
		}
		state.setRemark(remark);
		// 提交处理线程异步更新数据库.
		this.container.getDevConfDataSenderService().submitClaaDevInitState(state);
	}
	
	
	public GPRSDevService getGprsDevService() {
		return gprsDevService;
	}

	public void setGprsDevService(GPRSDevService gprsDevService) {
		this.gprsDevService = gprsDevService;
	}

	public IotLocUpdataProcessService getLocUpdataProcessService() {
		return locUpdataProcessService;
	}

	public void setLocUpdataProcessService(IotLocUpdataProcessService locUpdataProcessService) {
		this.locUpdataProcessService = locUpdataProcessService;
	}
	
}
